﻿/*
 * fbasync - An Asynchronous Facebook API for .NET
 * Copyright (C) 2007 ittybittysoft
 * Email: info@ittybittysoft.com
 * Portions of the library are based on the Facebook Developer Toolkit
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
using System;
using System.Collections.Generic;
using System.Xml;
using System.Net;
using System.Text;

namespace fbasync
{
    public static class Utility
    {
        public static readonly DateTime UnixStart = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);

        public static TAsyncState End<TAsyncState>(this IAsyncResult ar)
            where TAsyncState : class, new()
        {
            LazyAsyncResult<TAsyncState> myAr = ar as LazyAsyncResult<TAsyncState>;
            if (null == myAr)
                throw new ArgumentNullException("ar");

            if (!myAr.IsCompleted)
                myAr.AsyncWaitHandle.WaitOne();

            if (null != myAr.AsyncException)
                throw new AsynchronousException(myAr.AsyncException);

            return myAr.MyAsyncState;
        }

        public static bool? GetChildBool(this XmlElement parent, string childNode)
        {
            string val = GetChildText(parent, childNode) ?? "0";

            return val.Equals("1", StringComparison.Ordinal);
        }

        public static string GetChildText(this XmlElement parent, string childNode)
        {
            if (parent == null) return default(string);
            if (childNode == null) throw new ArgumentNullException("childNode");

            XmlElement child = parent[childNode, FacebookConnection.FacebookNamespace];

            return null == child ? default(string) : child.InnerText;
        }

        public static long? GetChildLong(this XmlElement parent, string childNode)
        {
            XmlElement child = parent[childNode, FacebookConnection.FacebookNamespace];

            return null == child || string.IsNullOrEmpty(child.InnerText) ? default(long?) : long.Parse(child.InnerText);
        }

        public static int? GetChildInt(this XmlElement parent, string childNode)
        {
            XmlElement child = parent[childNode, FacebookConnection.FacebookNamespace];

            return null == child || string.IsNullOrEmpty(child.InnerText) ? default(int?) : int.Parse(child.InnerText);
        }

        public static DateTime? GetChildUnixTime(this XmlElement parent, string childNode)
        {
            string time = GetChildText(parent, childNode);

            return string.IsNullOrEmpty(time) ? null : FromUnixTime(time);
        }

        public static double? GetChildDouble(this XmlElement parent, string childNode)
        {
            XmlElement child = parent[childNode, FacebookConnection.FacebookNamespace];

            return null == child || string.IsNullOrEmpty(child.InnerText) ? default(double?) : double.Parse(child.InnerText);
        }

        public static float? GetChildFloat(this XmlElement parent, string childNode)
        {
            XmlElement child = parent[childNode, FacebookConnection.FacebookNamespace];

            return null == child || string.IsNullOrEmpty(child.InnerText) ? default(float?) : float.Parse(child.InnerText);
        }

        public static DateTime? FromUnixTime(this string unixTime)
        {
            if (null == unixTime)
            {
                return null;
            }
            else
            {
                double time = double.Parse(unixTime);
                return FromUnixTime(time);
            }
        }

        public static DateTime FromUnixTime(this double unixTime)
        {
            return UnixStart.AddSeconds(unixTime);
        }

        public static List<T> GetFqlResults<T>(this XmlDocument fqlDoc, bool keepXml, Func<XmlElement, T> resultBuilder)
            where T: FacebookEntity
        {
            List<T> results = new List<T>();

            foreach (XmlNode n in fqlDoc.DocumentElement.ChildNodes)
            {
                if (n.NodeType == XmlNodeType.Element)
                {
                    XmlElement e = (XmlElement)n;
                    T result = resultBuilder(e);
                    if (keepXml) result.FqlResult = e;
                    results.Add(result);
                }
            }

            return results;
        }

        public static List<T> GetFqlResults<T>(this XmlDocument fqlDoc, Func<XmlElement, T> resultBuilder)
        {
            List<T> results = new List<T>();

            foreach (XmlNode n in fqlDoc.DocumentElement.ChildNodes)
            {
                if (n.NodeType == XmlNodeType.Element)
                {
                    XmlElement e = (XmlElement)n;
                    T result = resultBuilder(e);
                    results.Add(result);
                }
            }

            return results;
        }

        public static T GetSingleResult<T>(this XmlDocument fqlDoc, bool keepXml, Func<XmlElement, T> resultBuilder)
            where T : FacebookEntity
        {
            T result = resultBuilder(fqlDoc.DocumentElement);
            if (keepXml) result.FqlResult = fqlDoc.DocumentElement;
            return result;
        }

        public static string ToJsonAssociativeArray(this IDictionary<string, string> paramDictionary)
        {
            List<string> nameValuePairs = new List<string>();

            foreach (KeyValuePair<string, string> pair in paramDictionary)
            {
                // todo: ponder about it. the line marks unset all null values.
                if (pair.Value == null) continue;

                nameValuePairs.Add(String.Format("\"{0}\":{2}{1}{2}",
                    EscapeJsonString(pair.Key),
                    EscapeJsonString(pair.Value),
                    (pair.Value.IsJSONArray() || pair.Value.IsBoolean()) ? String.Empty : "\""));
            }

            return "{" + string.Join(",", nameValuePairs.ToArray()) + "}";
        }

        public static string ToJsonArray(this IEnumerable<string> paramList)
        {
            var br = new StringBuilder();
            br.Append("[");
            foreach (string str in paramList)
            {
                if (str == null)
                {
                    br.Append("null,");
                    continue;
                }

                br.Append(String.Format("{0}{1}{0},", (str.IsJSONArray() || str.IsBoolean()) ? String.Empty : "\"", str.EscapeJsonString()));
            }
            br.Replace(",", "]", br.Length - 1, 1);
            return br.ToString();
        }

        public static bool IsJSONArray(this string test)
        {
            if (String.IsNullOrEmpty(test)) return false;
            return (test.StartsWith("{") && !test.StartsWith("{*")) || test.StartsWith("[");
        }

        public static bool IsBoolean(this string test)
        {
            if (String.IsNullOrEmpty(test)) return false;
            return test.Equals("false", StringComparison.OrdinalIgnoreCase) ?
                true : test.Equals("true", StringComparison.OrdinalIgnoreCase);
        }

        private static string EscapeJsonString(this string originalString)
        {
            if (originalString.IsJSONArray())
                return originalString;

            // escape backslashes and double quotes
            return originalString.Replace("\\", "\\\\").Replace("\"", "\\\"");
        }

        public static void AddImages(this IDictionary<string, string> fbArgs, IDictionary<string, string> images)
        {
            if (null != images)
            {
                int i = 0;
                foreach (var kvp in images)
                    AddImageArg(fbArgs, ++i, kvp.Key, kvp.Value);
            }
        }

        private static void AddImageArg(IDictionary<string, string> args, int id, string imageUrl, string hyperlinkUrl)
        {
            if (null == args) throw new ArgumentNullException("args");

            if (!string.IsNullOrEmpty(imageUrl) && !string.IsNullOrEmpty(hyperlinkUrl))
            {
                args.Add("image_" + id, imageUrl);
                args.Add("image_" + id + "_link", hyperlinkUrl);
            }
        }
    }
}
